package net.uni.ap.dao;

import java.sql.Connection;

import javax.sql.DataSource;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.DaoSupport;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

public abstract class BaseDaoSupport extends DaoSupport {
	private HibernateTemplate hibernateTemplate;
	private JdbcTemplate jdbcTemplate;

	/**
	 * Initialize the template-based configuration of this DAO.
	 * Called after a new JdbcTemplate has been set, either directly
	 * or through a DataSource.
	 */
	protected void initTemplateConfig() {
	}
	/**
	 *  Set the JDBC DataSource to be used by this DAO.
	 * @param dataSource
	 */
	public final void setDataSource(DataSource dataSource) {
		if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
			this.jdbcTemplate = createJdbcTemplate(dataSource);
			initTemplateConfig();
		}
	}

	/**
	 * Create a JdbcTemplate for the given DataSource.
	 * @param dataSource
	 * @return new JdbcTemplate instance
	 */
	protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}
	/**
	 *  Return the JDBC DataSource used by this DAO.
	 */
	public final DataSource getDataSource() {
		return (this.jdbcTemplate != null ? this.jdbcTemplate.getDataSource() : null);
	}

	/**
	 * Return the JdbcTemplate used by this DAO.
	 */
	public final JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	/**
	 * Set the JdbcTemplate for this DAO explicitly
	 * @param jdbcTemplate
	 */
	public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
		initTemplateConfig();
	}

	/**
	 * Return the SQLExceptionTranslator of this DAO's JdbcTemplate,
	 * for translating SQLExceptions in custom JDBC access code.
	 * @return
	 */
	protected final SQLExceptionTranslator getExceptionTranslator() {
		return getJdbcTemplate().getExceptionTranslator();
	}

	/**
	 * Get a JDBC Connection from the current transaction or a new one.
	 * 事务控制由spring控制，杜绝自己写事务相关的代码
	 * @return the JDBC Connection
	 * @throws CannotGetJdbcConnectionException
	 */
	protected final Connection getConnection() throws CannotGetJdbcConnectionException {
		return DataSourceUtils.getConnection(getDataSource());
	}

	/**
	 * Close the given JDBC Connection, created via this DAO's DataSource,
	 * if it isn't bound to the thread.
	 * @param con Connection to close
	 * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
	 */
	protected final void releaseConnection(Connection con) {
		DataSourceUtils.releaseConnection(con, getDataSource());
	}

	/**
	 *  Set the HibernateTemplate for this DAO explicitly,
	 * @param hibernateTemplate
	 */
	public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}

	/**
	 * @return the HibernateTemplate for this DAO,
	 */
	public final HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	/**
	 * Set the Hibernate SessionFactory to be used by this DAO.
	 * Will automatically create a HibernateTemplate for the given SessionFactory.
	 * @param sessionFactory
	 */
	public final void setSessionFactory(SessionFactory sessionFactory) {
		if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) {
			this.hibernateTemplate = createHibernateTemplate(sessionFactory);
		}
	}

	/**
	 * Create a HibernateTemplate for the given SessionFactory.
	 * @param sessionFactory
	 * @return
	 */
	protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
		return new HibernateTemplate(sessionFactory);
	}

	/**
	 * Return the Hibernate SessionFactory used by this DAO.
	 */
	public final SessionFactory getSessionFactory() {
		return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null);
	}

	/**
	 * @return a Hibernate Session from the current transaction or a  new one.
	 * @throws DataAccessResourceFailureException
	 * @throws IllegalStateException
	 */
	protected final Session getSession()
			throws DataAccessResourceFailureException, IllegalStateException {
		return getSession(this.hibernateTemplate.isAllowCreate());
	}

	/**
	 * Close the given Hibernate Session, created via this DAO's SessionFactory, if it isn't bound to the thread
	 * @param session
	 */
	protected final void releaseSession(Session session) {
		SessionFactoryUtils.releaseSession(session, getSessionFactory());
	}

	protected final Session getSession(boolean allowCreate)
			throws DataAccessResourceFailureException, IllegalStateException {
		return (!allowCreate ? SessionFactoryUtils.getSession(
				getSessionFactory(), false) : SessionFactoryUtils.getSession(
				getSessionFactory(),
				this.hibernateTemplate.getEntityInterceptor(),
				this.hibernateTemplate.getJdbcExceptionTranslator()));
	}

	/**
	 * Convert the given HibernateException to an appropriate exception
	 * Typically used in plain Hibernate code with method Typically used in plain Hibernate code getSession
	 * and releaseSession
	 * @param ex
	 * @return
	 */
	protected final DataAccessException convertHibernateAccessException(HibernateException ex) {
		return this.hibernateTemplate.convertHibernateAccessException(ex);
	}

	@Override
	protected final void checkDaoConfig() throws IllegalArgumentException {
		if (this.hibernateTemplate == null && this.jdbcTemplate==null) {
			throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' or 'dataSource' or 'jdbcTemplate' is required");
		}
	}
}
